KMP 알고리즘

@VERO
Created Date · 2023년 11월 26일 08:11
Last Updated Date · 2023년 11월 29일 08:11

KMP 알고리즘이란?

문자열 A 안에 문자열 B가 들어있는지를 판단하는 알고리즘.

즉, 불일치가 발생하기 직전까지 같았던 부분은 다시 비교하지 않고 패턴 매칭을 진행하자는 것이다. 이를 통해 비교 횟수를 줄이고, 검색 알고리즘의 효율성을 높일 수 있다.

실패 함수 F(x)

문자열 S[0:x + 1] 에서 접두사와 접미사가 일치하는 최대 길이

실패 함수의 값은 접두사와 접미사가 일치하는 최대 길이를 의미하지만, 동시에 인덱스 관점에서 ==아직 확신할 수 없는== 다음 문자를 의미하기도 한다. (길이 = 인덱스 + 1)

실패 함수는 다음과 같은 과정으로 이해할 수 있다. (0-index 이다.)

failure-function.png

미래의 나에게 도움이 되기를 바라며...

코드

def make_fail_function(s):
	f = [0] * len(s)
	j = 0
	for i in range(1, len(s)):
		while j > 0 and s[i] != s[j]:
			j = f[j-1]
		if s[i] == s[j]:  # i 와 j 가 가리키는 글자가 같은지 확인한다.
			j += 1 # s[i]와 s[j] 가 같아지는 시점에서 f[j-1] 의 값 + 1 
			# f[j-1] 이란 s[0:j] 의 접두사와 접미사가 일치하는 최대 길이
			f[i] = j # f[i]에 일치하는 최대 길이를 저장한다.
	return f

KMP 알고리즘

실패 함수를 응용하면 KMP 알고리즘을 구현할 수 있다!!

def kmp(failure, a, b):  
    count = [0] * len(a)  
    result = []  
    j = 0  # j 는 b 를 가리키는 인덱스임과 동시에 일치하는 최대 길이가 된다.
    for i in range(len(a)):  # 모든 a 안의 index i 를 돌면서
        if a[i] == b[j]:  # 서로 같을 때
            j += 1  
            count[i] = j  # count[i]에 일치하는 최대 길이인 j 를 저장한다.
        else:  
            while j > 0 and a[i] != b[j]:  # 만약 a[i] 와 b[i] 가 같지 않다면 실패함수를 이용해서 계속해서 접두사와 접미사를 이동시킨다.
                j = failure[j - 1]  
            # 이 시점에서 j 가 0이거나, a[i] 와 b[j] 가 같게 된다.
            if a[i] == b[j]:  # 그 중 a[i] 와 b[j] 가 같은 경우는 마찬가지로 최대 길이를 저장해준다.
                j += 1  
                count[i] = j  
        if count[i] == len(b):  # 만약 b 를 끝까지 비교했는데 모두 같다면
            result.append(i - len(b) + 2)  # (선택) 같은 인덱스를 저장한다. (이때는 1-index 사용해서 +2를 해주었다. 취향 차이.)
            j = failure[j - 1]   # 바로 이전 문자의 접두사를 끌어온다.
    # 명심할 것. j 는 언제나 *다음 비교에서 실패할 수도 있는 b의 인덱스* 이다.
    return result

count 배열은 i에서 최대로 일치하는 문자열의 길이가 담겨있다.
즉, len(b) 가 count 에서 몇 번 나타나는지 알면 문자열이 최대 몇 개가 일치하는지 알 수 있는 것이다.

실패 함수의 성질

반복되는 문자열의 길이 L 은 문자열 길이 - failure[문자열 길이-1] 로 구할 수 있다

failure[문자열 길이 - 1] 은 맨 끝 문자까지의 부분 문자열에서 접두사이자 접미사인 최장 부분 문자열의 길이를 나타낸다.
근데 증명은 못하겠다… 너무 어려워요 나중에 이해되면 추가해보겠습니다.

백준 1305 번에서 사용됩니다

시간 복잡도

시간 복잡도는 O(N+M)O(N + M) 이다.

참고